package cn.shop.front.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.shop.front.config.WeChatConfig;
import cn.shop.front.controller.wx.utils.Constants;
import cn.shop.front.entity.gamebind.GameBind;
import cn.shop.front.entity.gamebind.RegistBindReq;
import cn.shop.front.entity.req.BindReq;
import cn.shop.front.entity.req.WxBindReq;
import cn.shop.front.service.FrontUserService;
import cn.shop.front.service.GameBindService;
import cn.shop.front.util.CurrentUserUtil;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.utils.ApiResult;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.SMSVerificationCode;
import cn.shop.validation.Save;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.shop.utils.ApiResult.fail;

/**
 * 游戏角色绑定
 *
 * @author : 李一涵
 * @date : Created in 2022-2-9 15:31
 * @description :
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/game")
public class GameBindController {

	@Resource
	private WeChatConfig weChatConfig;

	private final static Logger LOG = LoggerFactory.getLogger(GameBindController.class);

	@Resource
	private GameBindService gameBindService;

	@Resource
	private RestTemplate restTemplate;
	@Resource
	private FrontUserService frontUserService;

	/**
	 * 绑定
	 *
	 * @param req 绑定信息
	 * @return 返回结果
	 */
	@SaCheckLogin
	@PostMapping("/bind")
	public ApiResult<Map<String, Object>> bind(@Validated @RequestBody GameBind req) {
		req.setUserId(CurrentUserUtil.getUserId());
		return doBind(req);
	}

	/**
	 * 游戏内绑定
	 *
	 * @param req 绑定信息
	 * @return 返回结果
	 */
	@PostMapping("/gbind")
	public ApiResult<Map<String, Object>> gameBind(@Validated @RequestBody BindReq req) {
		Long gameId = gameBindService.getGameId(req.getPartitionId());
		if (gameId == null || gameId == 0L) {
			return ApiResult.fail("分区信息不存在, 请去网站绑定");
		}
		LambdaQueryWrapper<FrontUser> wrapper = Wrappers.lambdaQuery();
		wrapper.eq(FrontUser::getAccount, req.getUsername());
		FrontUser frontUser = frontUserService.getOne(wrapper);
		if (frontUser == null) {
			return ApiResult.fail("账户或密码错误");
		}
		String password = frontUser.getPassword();
		Assert.notBlank(password, "密码不能为空");
		boolean b = DigestUtil.bcryptCheck(req.getPassword(), password);
		if (!b) {
			return ApiResult.fail("账户或密码错误");
		}
		GameBind bind = new GameBind();
		bind.setGameId(gameId);
		bind.setAccount(req.getAccount());
		bind.setCode(req.getCode());
		bind.setNickname(URLDecoder.decode(req.getNickname(), StandardCharsets.UTF_8));
		bind.setPartitionId(req.getPartitionId());
		bind.setUserId(frontUser.getId());
		return doBind(bind);
	}

	/**
	 * 微信扫码注册。登录，绑定
	 * @param req 扫码信息
	 * @return 返回结果
	 */
	@PostMapping("/wxbind")
	public ApiResult<Map<String, Object>> wxLoginBind(@Validated WxBindReq req) {
		log.info("二维码绑定信息：{}", req);
		String[] infos = req.getGameInfo().split("の");
		if (infos.length < 4) {
			return ApiResult.fail("参数错误");
		}
		long partitionId = Long.parseLong(infos[0]);
		String account = infos[1];
		String nickname = infos[2];
		String gameCode = infos[3];
		JSONObject atObj = getAccessToken(req.getCode());
		String token = atObj.getStr("access_token");
		String unionid = atObj.getStr("unionid");
		String openid = atObj.getStr("openid");
		if (StrUtil.isBlank(token) || StrUtil.isBlank(unionid)) {
			return ApiResult.fail("微信扫码登录失败");
		}
		FrontUser wxuser = frontUserService.lambdaQuery()
				.eq(FrontUser::getUnionId, unionid)
				.one();
		Map<String, Object> registInfo = new HashMap<>();
		boolean isRegist = false;
		if (wxuser == null) {
			JSONObject obj = getWechartInfo(token, openid);
			String username = "wx" + RandomUtil.randomString(6);
			String password = RandomUtil.randomNumbers(8);
			wxuser = new FrontUser();
			wxuser.setAccount(username);
			wxuser.setPassword(password);
			wxuser.setAvatar(obj.getStr("headimgurl"));
			wxuser.setUnionId(unionid);
			wxuser.setCanRename(true);
			boolean success = frontUserService.insert(wxuser);
			if (!success) {
				return ApiResult.fail("系统错误：网站账户注册失败，请重试");
			}
			isRegist = true;
			registInfo = Putif.start()
					.anyway("account", username)
					.anyway("password", password)
					.get();
		}
		registInfo.put("isRegist", isRegist);
		Long gameId = gameBindService.getGameId(partitionId);
		if (gameId == null || gameId == 0L) {
			return ApiResult.fail("游戏信息不存在", registInfo);
		}
		FrontUser update = new FrontUser();
		update.setAvatar(wxuser.getAvatar());
		frontUserService.updateById(update);
		GameBind bind = new GameBind();
		bind.setGameId(gameId);
		bind.setAccount(account);
		bind.setCode(gameCode);
		bind.setNickname(URLDecoder.decode(nickname, StandardCharsets.UTF_8));
		bind.setPartitionId(partitionId);
		bind.setUserId(wxuser.getId());
		ApiResult<Map<String, Object>> result =  doBind(bind);
		result.setData(registInfo);
		return result;
	}

	/**
	 * 游戏内注册并绑定
	 *
	 * @param req 注册绑定信息
	 * @return 返回结果
	 * @author lyh
	 * @since 2022-07-21
	 */
	@PostMapping("/insert")
	public ApiResult<Map<String, Object>> insert(@Validated(Save.class) @RequestBody RegistBindReq req) {
		String key = SMSVerificationCode.REGIST_KEY + req.getPhone();
		SaSession session = SaSessionCustomUtil.getSessionById(key);
		Assert.notNull(session, "验证码已失效");
		String c = session.getString("code");
		if (StrUtil.isBlank(c) || !c.equals(req.getCode())) {
			return fail("验证码不正确");
		}
		session.clear();
		FrontUser frontUser = new FrontUser();
		frontUser.setAccount(req.getAccount());
		frontUser.setPassword(req.getPassword());
		frontUser.setPhone(req.getPhone());
		boolean isSuccess = frontUserService.insert(frontUser);
		if (!isSuccess) {
			return ApiResult.fail("账号注册失败，请重试");
		}
		Long gameId = gameBindService.getGameId(req.getPid());
		if (gameId == null || gameId == 0L) {
			return ApiResult.fail("分区信息不存在, 请去网站绑定");
		}
		GameBind bind = new GameBind();
		bind.setGameId(gameId);
		bind.setAccount(req.getGameAccount());
		bind.setCode(req.getGameCode());
		bind.setNickname(URLDecoder.decode(req.getGameNickname(), StandardCharsets.UTF_8));
		bind.setPartitionId(req.getPid());
		bind.setUserId(frontUser.getId());
		return doBind(bind);
	}

	private JSONObject getAccessToken(String code) {
		String params = "?appid=" + weChatConfig.getBindAppId() + "&secret=" + weChatConfig.getBindAppSecret() + "&code=" + code + "&grant_type=authorization_code";
		String url = Constants.WX_ACCESS_TOKEN_URL;
		String res = HttpUtil.get(url + params);
		log.info("游戏内扫码获取 微信access_token : {}", res);
		return JSONUtil.parseObj(res);
	}

	private JSONObject getWechartInfo(String token, String openId) {
		String params = "?access_token=" + token + "&openid=" + openId + "&lang=zh_CN";
		String url = Constants.WX_USERINFO_URL;
		String res = HttpUtil.get(url + params);
		log.info("通过请求获取 微信用户信息 : {}", res);
		return JSONUtil.parseObj(res);
	}

	/**
	 * 解绑
	 *
	 * @param id 编号
	 * @return 返回结果
	 */
	@SaCheckLogin
	@DeleteMapping("/unbind/{id}")
	public ApiResult<Map<String, Object>> unbind(@PathVariable long id) {
		GameBind unbind = new GameBind();
		unbind.setId(id);
		GameBind bind = gameBindService.getById(id);
		if (bind == null) {
			return ApiResult.fail();
		}
		Map<String, Object> query = Putif.start()
				.anyway("partitionId", bind.getPartitionId())
				.anyway("account", bind.getAccount())
				.anyway("nickname", bind.getNickname())
				.anyway("code", bind.getCode())
				.get();
		try {
			restTemplate.postForObject("http://localhost:83/game/api/bind/unbind", query, String.class);
		} catch (Exception ignored) {
			LOG.info("删除游戏服务器上绑定信息出错");
		}
		return ApiResult.result(gameBindService.removeById(unbind));
	}

	/**
	 * 已绑定列表
	 *
	 * @return 返回结果
	 */
	@SaCheckLogin
	@GetMapping("/list")
	public ApiResult<List<Map<String, Object>>> list() {
		List<Map<String, Object>> data = gameBindService.bindList(CurrentUserUtil.getUserId());
		return ApiResult.success(data);
	}

	/**
	 * 获取所有游戏
	 *
	 * @return 返回结果
	 */
	@GetMapping("/allGame")
	public ApiResult<List<Map<String, Object>>> allGame() {
		return ApiResult.success(gameBindService.allGame());
	}

	/**
	 * 获取游戏下所有分区
	 *
	 * @param gameId 游戏编号
	 * @return 返回结果
	 */
	@GetMapping("/allPartitionByGame/{gameId}")
	public ApiResult<List<Map<String, Object>>> allPartitionByGame(@PathVariable long gameId) {
		return ApiResult.success(gameBindService.allPartitionByGame(gameId));
	}

	@SuppressWarnings("unchecked")
	private ApiResult<Map<String, Object>> doBind(GameBind req) {
		log.info("绑定信息：{}", req);
		long count = gameBindService.lambdaQuery()
				.eq(GameBind::getAccount, req.getAccount())
				.eq(GameBind::getNickname, req.getNickname())
				.eq(GameBind::getPartitionId, req.getPartitionId())
				.count();
		log.info("查询是否绑定过：{}", count);
		if (count > 0) {
			return ApiResult.fail("请检查验证码和角色名是否匹配，或者是否已经绑定过");
		}
		Map<String, Object> query = Putif.start()
				.anyway("partitionId", req.getPartitionId())
				.anyway("account", req.getAccount())
				.anyway("nickname", req.getNickname())
				.anyway("code", req.getCode())
				.get();
		Map<String, Object> result = restTemplate.postForObject("http://localhost:83/game/api/bind/checkCode", query, Map.class);
		LOG.info("检查验证码返回结果：{}", result);
		if (result != null && result.get("data") != null && !(Boolean) result.get("data")) {
			return ApiResult.fail("请检查验证码和角色名是否匹配，或者是否已经绑定过");
		}
		boolean success = gameBindService.save(req);
		if (success) {
			restTemplate.postForObject("http://localhost:83/game/api/bind/bindSuccess", query, String.class);
		}
		return ApiResult.result(success);
	}

}
